package strategies;

//~--- non-JDK imports --------------------------------------------------------

import com.dukascopy.api.IContext;
import com.dukascopy.api.IOrder;
import com.dukascopy.api.JFException;

//~--- JDK imports ------------------------------------------------------------

import java.util.Date;
import java.util.List;
import java.util.ListIterator;

/**
 * This strategy uses a progressive method to calculate the profit
 */
public class AdaptiveTakeProfitStrategy extends Strategy {
    private SarLine uslLine;
    private SarLine lslLine;
    private SarLine uflLine;
    private SarLine lflLine;

    /**
     * Creates new instance of ConcreteStrategy
     * @param arguments strategy arguments
     */
    public AdaptiveTakeProfitStrategy(StrategyArgs arguments) {
        super(arguments);

        // Its always a trading zone once steady state is achieved.
        // Hence default tradingZone to true
        tradingZone = true;
    }

    @Override
    public void onStart(IContext context) throws JFException {
        super.onStart(context);

        // Initialize sar lines here
        uslLine = SarLine.sarMap.get(this).get("USL");
        lslLine = SarLine.sarMap.get(this).get("LSL");
        uflLine = SarLine.sarMap.get(this).get("UFL");
        lflLine = SarLine.sarMap.get(this).get("LFL");
    }

    /**
     * Evaluate the trade entry
     * @throws JFException
     */
    protected void evaluateTradeEntry() throws JFException {
        // Look for LONG state...
        if (state == States.READY_TOGO_SHORT || state == States.LOOK_FOR_SHORT || state == States.NEXT_ONLY_LONG
                || state == States.START) {
            if (uslLine.isBreached(latestTick)) {
                state = States.LOOK_FOR_LONG;
                uslLine.resetExtremePoint(latestTick.getBid());
            }
        }

        if (state == States.LOOK_FOR_LONG) {
            if (uslLine.getCandlesAfterEP() > arguments.getCandleThreshold()) {
                state = States.READY_TOGO_LONG;
                uslLine.setTradeEntryThreshold(uslLine.getExtremePoint());
            }
        }

        if (state == States.LOOK_FOR_LONG) {
            if (lflLine.isBreached(latestTick)) {
                state = States.START;
            }
        }

        if (state == States.READY_TOGO_LONG) {
            if (latestTick.getBid() > (uslLine.getTradeEntryThreshold() + BREACH_THRESHOLD)) {
                buy(localInstrument);
                uflLine.setTradeEntryThreshold(0);
            }
        }

        // SHORT
        if ((state == States.READY_TOGO_LONG) || (state == States.LOOK_FOR_LONG) || (state == States.NEXT_ONLY_SHORT)
                || (state == States.START)) {
            if (lslLine.isBreached(latestTick)) {
                state = States.LOOK_FOR_SHORT;
                lslLine.resetExtremePoint(latestTick.getBid());

                // context.getConsole().getOut().println(state.toString());
            }
        }

        if (state == States.LOOK_FOR_SHORT) {
            if (lslLine.getCandlesAfterEP() > arguments.getCandleThreshold()) {
                state = States.READY_TOGO_SHORT;
                lslLine.setTradeEntryThreshold(lslLine.getExtremePoint());
            }
        }

        if (state == States.LOOK_FOR_SHORT) {
            if (uflLine.isBreached(latestTick)) {
                state = States.START;
            }
        }

        if (state == States.READY_TOGO_SHORT) {
            if (latestTick.getBid() < (lslLine.getTradeEntryThreshold() - BREACH_THRESHOLD)) {
                sell(localInstrument);
                lflLine.setTradeEntryThreshold(0);
            }
        }
    }

    protected void evaluateTradeExit() throws JFException {
        if (positionsTotal(localInstrument) > 0) {
            boolean useStopLoss             = (arguments.getStopLoss() > 0)
                                              ? true
                                              : false;
            boolean useTakeProfit           = (arguments.getTakeProfit() > 0);
            boolean isShort                 = (state == States.SHORT);
            boolean isLong                  = (state == States.LONG);
            double  longProfitOrLossInPips  = (latestTick.getBid() - orderOpenPrice) * 10000;
            double  shortProfitOrLossInPips = (orderOpenPrice - latestTick.getAsk()) * 10000;

            if ((isShort
                    && (canTakeProfit()
                        || (uflLine.isBreached(latestTick))
                        || (useStopLoss && (-shortProfitOrLossInPips >= arguments.getStopLoss())))) || (isLong
                            && (canTakeProfit()
                                || (lflLine.isBreached(latestTick))
                                || (useStopLoss && (-longProfitOrLossInPips >= arguments.getStopLoss()))))) {

                // Cover any open positions...
                List<IOrder> orders = engine.getOrders(localInstrument);

                if (orders.size() > 1) {
                    context.getConsole().getOut().println("Error more than one pending order: " + orders.size());
                }

                ListIterator<IOrder> orderIter = orders.listIterator();

                while (orderIter.hasNext()) {
                    IOrder order = orderIter.next();

                    engine.closeOrders(order);
                    orderClosePrice = (state == States.SHORT)
                                      ? latestTick.getAsk()
                                      : latestTick.getBid();
                    orderCloseTime  = latestTick.getTime();

                    Date tempCloseTime = new Date(orderCloseTime);

                    daysProfit += (state == States.SHORT)
                                  ? (orderOpenPrice - orderClosePrice - BROKERAGE)
                                  : (orderClosePrice - orderOpenPrice - BROKERAGE);
                    context.getConsole().getOut().println(tempCloseTime.toGMTString() + ", COVER-" + state.toString()
                            + "," + account.getEquity());

                    int conseqTrades = (state == States.LONG)
                                       ? conseqLongs
                                       : conseqShorts;

                    if (conseqTrades >= arguments.getMaxConsecutivePositions()) {
                        state = (state == States.LONG)
                                ? States.NEXT_ONLY_SHORT
                                : States.NEXT_ONLY_LONG;
                    } else {
                        state = (state == States.LONG)
                                ? States.LOOK_FOR_LONG
                                : States.LOOK_FOR_SHORT;
                    }
                }
            }
        }
    }

  private boolean canTakeProfit() {
    double  longProfitOrLossInPips  = (latestTick.getBid() - orderOpenPrice) * 10000;
    double  shortProfitOrLossInPips = (orderOpenPrice - latestTick.getAsk()) * 10000;

    if (state == States.LONG && longProfitOrLossInPips > arguments.getMinTakeProfit() && (latestTick.getBid() <= uflLine.getTradeExitThreshold()))
      return true;

    if (state == States.SHORT && shortProfitOrLossInPips > arguments.getMinTakeProfit() && lflLine.getTradeExitThreshold() != 0 && (latestTick.getBid() >= lflLine.getTradeExitThreshold()))
      return true;
    return false;  //To change body of created methods use File | Settings | File Templates.
  }
}


//~ Formatted by Jindent --- http://www.jindent.com
